from descriptionkinds import Managed

# 属性描述符
# 1、描述符示例：验证属性
# @see bulkfood_v3.py
# 自动获取储存属性的名称
# @see bulkfood_v4.py bulkfood_v4c.py model_v4c.py
# 不同描述符验证功能
# @see model_v5.py

# 2、覆盖型与非覆盖型描述符对比
# @see descriptionkinds.py
# 实现 __set__ 方法的描述符属于覆盖型描述符
obj = Managed()
obj.over
Managed.over
obj.over = 7
obj.over
obj.__dict__['over'] = 8
print(vars(obj))
obj.over  # Managed.over 描述符仍会覆盖读取 obj.over 这个操作
# 没有 __get__ 方法的覆盖型描述符
# 读 实例属性会遮盖描述符
print("-----------------------------no_get------------------")
print(obj.over_no_get)
print(Managed.over_no_get)
obj.over_no_get = 7
print(obj.over_no_get)  # __set__ 方法没有修改属性，所以在此读取 obj.over_no_get获取的仍是托管类中的描述符实例
obj.__dict__['over_no_get'] = 9
print(obj.over_no_get)  # 遮盖描述符
obj.over_no_get = 7  # 赋值 仍然经过描述符的__set__ 方法处理
print(obj.over_no_get)
# 非覆盖型描述符
print('------------NonOverriding-----------------')
obj.non_over
obj.non_over = 7
print(obj.non_over)  # obj 有个名为 non_over 的实例属性，把 Managed 类的同名描述符属性遮盖掉
Managed.non_over  # Managed.non_over 描述符依然存在，会通过类截获这次访问
del obj.non_over
obj.non_over

# 在类中覆盖描述符
# 不管描述符是不是覆盖型，为类属性赋值都能覆盖描述符
# 类读取依附描述符__get__ 写不依附__set__
obj = Managed()
Managed.over = 1
Managed.over_no_get = 2
Managed.non_over = 3
print(obj.over, obj.over_no_get, obj.non_over)

# 3、方法是描述符
# 在类中定义的函数属于绑定方法（bound method），因为用户定义的函数都有 __get__ 方法，所以依附到类上时，就相当于描述符
print('--------3------')
obj = Managed()
print(obj.spam)
print(Managed.spam)
obj.spam = 7  # 果为 obj.spam 赋值，会遮盖类属性，导致无法通过 obj 实例访问 spam 方法
print(obj.spam)  # 函数没有实现 __set__ 方法，因此是非覆盖型描述符
# @see method_is_descriptor.py
# 4、描述符用法建议
# 使用特性以保持简单
# 内置的 property 类创建的其实是覆盖型描述符，__set__ 方法和# __get__ 方法都实现了，即便不定义设值方法也是如此。
# 特性的__set__ 方法默认抛出 AttributeError: can't set attribute，因此创建只读属性最简单的方式是使用特性
# 只读描述符必须有 __set__ 方法
# 如果使用描述符类实现只读属性，要记住，__get__ 和 __set__
# 两个方法必须都定义，否则，实例的同名属性会遮盖描述符。
# 只读属性的 __set__ 方法只需抛出 AttributeError 异常，并提供合适的错误消息。
# 用于验证的描述符可以只有 __set__ 方法
# 对仅用于验证的描述符来说，__set__ 方法应该检查 value 参数获得的值，
# 如果有效，使用描述符实例的名称为键，直接在实例的 __dict__ 属性中设置。
# 这样从实例中读取同名属性的速度很快，因为不用经过 __get__ 方法处理。
# 仅有 __get__ 方法的描述符可以实现高效缓存
# 如果只编写了 __get__ 方法，那么创建的是非覆盖型描述符。这种描述符可用于执行某些耗费资源的计算，然后为实例设置同名属性，
# 缓存结果。同名实例属性会遮盖描述符，因此后续访问会直接从实例的__dict__ 属性中获取值，而不会再触发描述符的 __get__ 方法。
# 非特殊的方法可以被实例属性遮盖
# 由于函数和方法只实现了 __get__ 方法，它们不会处理同名实例属性的赋值操作。
# 因此，像 my_obj.the_method = 7 这样简单赋值之后，后续通过该实例访问 the_method 得到的是数字 7
# 但是不影响类或其他实例

# 5、描述符的文档字符串和覆盖删除操作
# 删除 -> 描述符类实现 __delete__ 方法